import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TreeModule } from './tree.module';

@Component({
  template: `
    <lv-tree
      [lvData]="data"
      [lvAsync]="async"
      [lvSelection]="selection"
      [lvShowLine]="showLine"
      [lvShowContentIcon]="showContentIcon"
      [lvShowExpandedIcon]="showExpandedIcon"
      [lvShowCheckbox]="showCheckbox"
      [lvSelectionMode]="selectionMode"
      [lvSelectedValueMode]="selectedValueMode"
      [lvSelectionAssociate]="selectionAssociate"
      [lvSelectedByCheckbox]="selectedByCheckbox"
      [lvCompareWith]="compareWith"
      [lvBeforeSelected]="beforeSelected"
      (lvExpandedChange)="expandedChange($event)"
      (lvSelectionChange)="selectionChange($event)"
      (lvClick)="click()"
      (lvSelect)="select()"
      (lvCheck)="check()"
    ></lv-tree>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
class TreeComponent {
  data = [
    {
      label: 'root node 1',
      expanded: true,
      children: [
        {
          label: 'child node 1-1 long long long long long long long long long long long long name.doc',
          expanded: true,
          contentToggleIcon: 'icon-storage',
          children: [
            { label: 'Leaf node 1-1-1', contentToggleIcon: 'icon-storage', isLeaf: true },
            { label: 'Leaf node 1-1-2', contentToggleIcon: 'icon-storage', isLeaf: true },
          ],
        },
        {
          label: 'child node 1-2',
          children: [
            { label: 'Leaf node 1-2-1', isLeaf: true },
            { label: 'Leaf node 1-2-2', showCheckbox: false, contentToggleIcon: '', isLeaf: true },
          ],
        },
      ],
    },
    {
      label: 'root node 2',
      children: [
        {
          label: 'Leaf node 2-1',
          isLeaf: true,
        },
        {
          label: 'child node 2-2',
          disabled: true,
          children: [
            { label: 'Leaf node 2-2-1', isLeaf: true },
            { label: 'Leaf node 2-2-2', isLeaf: true },
          ],
        },
      ],
    },
  ];
  selection = [];
  showContentIcon = false;
  showExpandedIcon = true;
  showCheckbox = false;
  selectionMode = 'multiple';
  showLine = false;
  selectedValueMode = 'both';
  selectionReturn = [];
  selectionAssociate = true;
  selectedByCheckbox = false;
  compareWith;
  beforeSelected;
  async = false;
  expandedChange(node) {
    if (this.async) {
      setTimeout(() => {
        node.children = [
          {
            label: 'test1',
            isLeaf: true,
          },
          {
            label: 'test2',
            isLeaf: true,
          },
          {
            label: 'test3',
            isLeaf: true,
          },
        ];
        this.data = [...this.data];
        this.selection = [this.data[0].children[1].children[0]];
      }, 1000);
    }
  }
  selectionChange(e) {
    this.selectionReturn = e;
  }
  click() {}
  select() {}
  check() {}
}

describe('TreeComponent', () => {
  beforeEach(
    waitForAsync(() => {
      TestBed.configureTestingModule({
        declarations: [TreeComponent],
        imports: [TreeModule, NoopAnimationsModule],
        teardown: { destroyAfterEach: false },
      }).compileComponents();
    })
  );
  describe('render', () => {
    let component: TreeComponent;
    let fixture: ComponentFixture<TreeComponent>;
    const renderComp = fakeAsync(para => {
      for (const [key, value] of Object.entries(para)) {
        component[key] = value;
      }
      fixture.detectChanges();
      tick(1500);
      fixture.detectChanges();
      expect(fixture).toMatchSnapshot();
      tick(1500);
    });

    beforeEach(() => {
      fixture = TestBed.createComponent(TreeComponent);
      component = fixture.componentInstance;
    });

    // 自定义节点展开图标渲染
    it('with lvShowExpandedIcon', () => {
      renderComp({ showExpandedIcon: "{ expandIcon: 'lv-icon-arrow-down', collapseIcon: 'lv-icon-arrow-right' }" });
    });

    // 自定义节点内容前图标渲染
    it('with contentToggleIcon and lvShowContentIcon', () => {
      renderComp({ showContentIcon: true });
    });

    // 显示checkbox渲染
    it('with showCheckbox', () => {
      renderComp({ showCheckbox: true });
    });

    // 连接线不展示渲染
    it('with lvShowLine', () => {
      renderComp({ lvShowLine: false });
    });
  });

  describe('work', () => {
    let component: TreeComponent;
    let fixture: ComponentFixture<TreeComponent>;

    const getContent = () => fixture.nativeElement.querySelectorAll('.lv-treenode-content');

    beforeEach(fakeAsync(() => {
      fixture = TestBed.createComponent(TreeComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      tick(500);
    }));

    // 节点能正常展开，正常折叠，触发lvExpandedChange回调
    it('when expanded and trigger lvExpandedChange', fakeAsync(() => {
      jest.spyOn(component, 'expandedChange');
      fixture.detectChanges();
      expect(getContent().length).toBe(6);

      const expandIcon = fixture.nativeElement.querySelectorAll('.lv-treenode-toggle-expanded');
      expandIcon[2].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      expect(getContent().length).toBe(8);
      expect(component.expandedChange).toBeCalledTimes(1);

      expandIcon[2].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      tick(1500);
      expect(getContent().length).toBe(6);
      expect(component.expandedChange).toBeCalledTimes(2);
    }));

    // 单选生效（lvSelection默认选中项生效），lvSelectionChange选中回调
    it('correctly with lvSelectionMode pass single', fakeAsync(() => {
      jest.spyOn(component, 'selectionChange');
      jest.spyOn(component, 'click');
      component.selectionMode = 'single';
      component.selection = [component.data[0]];
      fixture.detectChanges();

      const nodeSelected = fixture.nativeElement.querySelector('.lv-treenode-selected');
      expect(nodeSelected.textContent).toContain('root node 1');

      const content = getContent()[2];
      content.dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      const nodeSelected2 = fixture.nativeElement.querySelector('.lv-treenode-selected');
      expect(nodeSelected2.textContent).toContain('Leaf node 1-1-1');
      expect(component.selectionChange).toBeCalled();
      expect(component.click).toBeCalled();
    }));

    // 多选生效（lvSelection默认选中项生效），lvSelectedValueMode只返回父节点，lvSelectionChange选中回调
    it('correctly with lvSelectionMode pass multiple', fakeAsync(() => {
      jest.spyOn(component, 'selectionChange');
      jest.spyOn(component, 'select');
      jest.spyOn(component, 'check');
      component.selectedValueMode = 'parent';
      component.selection = [component.data[0].children[0].children[0], component.data[0].children[0].children[1]];
      fixture.detectChanges();

      const nodeSelected = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected.length).toEqual(2);

      const content = getContent();
      content[1].dispatchEvent(new Event('click'));
      content[4].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      // 当子节点全部选中时，只返回父节点
      expect(component.selectionReturn.length).toBe(1);
      expect(component.selectionReturn[0].label).toBe('root node 1');
      expect(component.selectionChange).toBeCalled();
      expect(component.select).toBeCalled();
      expect(component.check).toBeCalled();
    }));

    // 多选生效，lvSelectedValueMode只返回子节点
    it('correctly with lvSelectedValueMode pass child', fakeAsync(() => {
      component.selectedValueMode = 'child';
      fixture.detectChanges();

      const content = getContent();
      content[1].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      // 当父节点选中时，只返回所有子节点
      expect(component.selectionReturn.length).toBe(2);
    }));

    // lvSelectionAssociate传入为false时，父子节点非关联选中
    test('correctly with lvSelectionAssociate', fakeAsync(() => {
      component.selectionAssociate = false;
      fixture.detectChanges();

      const content = getContent();
      content[2].dispatchEvent(new Event('click'));
      content[3].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      // 选中所有子节点，但不会选中父节点
      const nodeSelected = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected.length).toEqual(2);

      content[0].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      // 选中父节点，但不会选中子节点
      const nodeSelected2 = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected2.length).toEqual(3);
    }));

    // lvSelectedByCheckbox只能checkbox选中
    test('correctly with lvSelectedByCheckbox', fakeAsync(() => {
      component.selectedByCheckbox = true;
      fixture.detectChanges();

      const content = getContent();
      content[1].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();

      // 非checkbox无法选中
      const nodeSelected = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected.length).toEqual(0);
    }));

    // lvCompareWith传入不同值生效
    test('correctly with lvCompareWith', fakeAsync(() => {
      component.compareWith = 'label';
      fixture.detectChanges();

      const content = getContent();
      content[1].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();

      const nodeSelected = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected.length).toEqual(3);
    }));

    // lvBeforeSelected选中前回调触发，传入false阻止选中
    it('correctly with lvBeforeSelected', fakeAsync(() => {
      component.beforeSelected = () => false;
      fixture.detectChanges();

      const content = getContent();
      content[1].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();

      const nodeSelected = fixture.nativeElement.querySelectorAll('.lv-treenode-selected');
      expect(nodeSelected.length).toEqual(0);
    }));

    // 异步数据树功能正确，lvAsync（同步和异步切换）
    test('correctly with lvAsync', fakeAsync(() => {
      component.showCheckbox = true;
      component.selection = [component.data[0].children[1].children[0]];
      fixture.detectChanges();
      tick(1500);
      fixture.detectChanges();

      // 切换异步后展开操作
      component.async = true;
      const expandIcon = fixture.nativeElement.querySelectorAll('.lv-treenode-toggle-expanded');
      expandIcon[2].dispatchEvent(new Event('click'));
      tick(1500);
      fixture.detectChanges();
      tick(1500);
      fixture.detectChanges();
      tick(1500);
      expect(getContent().length).toBe(9);
      const nodeSelected = fixture.nativeElement.querySelector('.lv-treenode-selected');
      expect(nodeSelected.textContent).toEqual('test1');
    }));

    // TODO 虚拟滚动场景测试lvVirtualScroll，lvVirtualItemSize，lvVirtualMaxBuffer，lvVirtualMinBuffer，lvTrackBy（当前UT环境不支持）
  });
});
